#include "gpu.h"

CvSeq* FaceDetectionGPU(IplImage *image, IplImage *gray_img, CvHaarClassifierCascade *cascade, CvMemStorage *storage, float scale_factor) {

   if(!cascade->hid_cascade) {
        printf("face_detection_gpu::FaceDetectionGPU: no hid_cascade, create\n");
        icvCreateHidHaarClassifierCascade(cascade);
   }

    //to delete after combined with preprocessing

    IplImage *sum_image = cvCreateImage(cvSize(gray_img->width + 1, gray_img->height + 1), IPL_DEPTH_32S, 1);
    IplImage *sum_float_image = cvCreateImage(cvSize(gray_img->width + 1, gray_img->height + 1), IPL_DEPTH_32F, 1);
    IplImage *sqsum_image = cvCreateImage(cvSize(gray_img->width + 1, gray_img->height + 1), IPL_DEPTH_64F, 1);
    IplImage *sqsum_float_image = cvCreateImage(cvSize(gray_img->width + 1, gray_img->height + 1), IPL_DEPTH_32F, 1);

    unsigned char *image_data = (unsigned char *)image->imageData;
    unsigned char *gray_img_data = (unsigned char *)gray_img->imageData;
    float *sum_data = (float *)sum_float_image->imageData;
    float *sqsum_data = (float *)sqsum_float_image->imageData;

    fprintf(stderr, "face_detection_gpu_hsiao::FaceDetectionGPU: image: (%d, %d, %d, %d) sum: (%d, %d, %d, %d)\n",
            image->width, image->height, image->widthStep, image->widthStep / image->nChannels,
            sum_float_image->width, sum_float_image->height, sum_float_image->widthStep, sum_float_image->widthStep / sum_float_image->nChannels / sizeof(float));

    int img_cols = gray_img->width;
    int img_rows = gray_img->height;

    int img_width_step = image->widthStep / (sizeof(unsigned char) * image->nChannels);

    int sum_width = sum_image->width;
    int sum_height = sum_image->height;

    int sum_width_step = sum_image->widthStep / (sizeof(float) * sum_image->nChannels);

    GPUIntegralImage(gray_img_data, sum_data, sqsum_data, gray_img->width, gray_img->height, img_width_step, sum_width_step);

    //initialize cascade data
    int cascade_flags = cascade->flags;
    int cascade_count = cascade->count;
    int cascade_origin_window_size_width = cascade->orig_window_size.width;
    int cascade_origin_window_size_height = cascade->orig_window_size.height;
    int cascade_real_window_size_width = cascade->real_window_size.width;
    int cascade_real_window_size_height = cascade->real_window_size.height;
    float cascade_scale = cascade->scale;
    fprintf(stderr, "cascade: (%d, %d, (%d, %d), (%d, %d) %f)\n", cascade_flags, cascade_count, cascade_origin_window_size_width, cascade_origin_window_size_height, cascade_real_window_size_width, cascade_real_window_size_height);

    int max_stage_count = 0;
    int total_classifiers = 0;
    CvHaarStageClassifier *stage_classifier = NULL;
    CvHaarClassifier *classifier = NULL;
    for(int i = 0; i < cascade_count; i++) {
        stage_classifier = cascade->stage_classifier + i;
        max_stage_count = Max(max_stage_count, stage_classifier->count);
        total_classifiers += stage_classifier->count;
        for(int j = 0; j < stage_classifier->count; j++)
            classifier = stage_classifier->classifier + j;
    }

    int *stage_count = (int *)malloc(cascade_count * sizeof(int));
    float *stage_threshold = (float *)malloc(cascade_count * sizeof(int));

    int *classifier_rect0_x = (int *)malloc(cascade_count * max_stage_count * sizeof(int));
    int *classifier_rect0_y = (int *)malloc(cascade_count * max_stage_count * sizeof(int));
    int *classifier_rect0_width = (int *)malloc(cascade_count * max_stage_count * sizeof(int));
    int *classifier_rect0_height = (int *)malloc(cascade_count * max_stage_count * sizeof(int));
    float *classifier_rect0_weight = (float *)malloc(cascade_count * max_stage_count * sizeof(float));

    int *classifier_rect1_x = (int *)malloc(cascade_count * max_stage_count * sizeof(int));
    int *classifier_rect1_y = (int *)malloc(cascade_count * max_stage_count * sizeof(int));
    int *classifier_rect1_width = (int *)malloc(cascade_count * max_stage_count * sizeof(int));
    int *classifier_rect1_height = (int *)malloc(cascade_count * max_stage_count * sizeof(int));
    float *classifier_rect1_weight = (float *)malloc(cascade_count * max_stage_count * sizeof(float));

    int *classifier_rect2_x = (int *)malloc(cascade_count * max_stage_count * sizeof(int));
    int *classifier_rect2_y = (int *)malloc(cascade_count * max_stage_count * sizeof(int));
    int *classifier_rect2_width = (int *)malloc(cascade_count * max_stage_count * sizeof(int));
    int *classifier_rect2_height = (int *)malloc(cascade_count * max_stage_count * sizeof(int));
    float *classifier_rect2_weight = (float *)malloc(cascade_count * max_stage_count * sizeof(float));

    float *classifier_alpha0 = (float *)malloc(cascade_count * max_stage_count * sizeof(float));
    float *classifier_alpha1 = (float *)malloc(cascade_count * max_stage_count * sizeof(float));
    float *classifier_threshold = (float *)malloc(cascade_count * max_stage_count * sizeof(float));

    for(int i = 0; i < cascade->count; i++) {
        stage_classifier = cascade->stage_classifier + i;
        stage_count[i] = stage_classifier->count;
        stage_threshold[i] = stage_classifier->threshold - ICV_STAGE_THRESHOLD_BIAS;
        for(int j = 0; j < stage_classifier->count; j++) {
            classifier = stage_classifier->classifier + j;
            classifier_rect0_x[i * max_stage_count + j] = classifier->haar_feature->rect[0].r.x;
            classifier_rect0_y[i * max_stage_count + j] = classifier->haar_feature->rect[0].r.y;
            classifier_rect0_width[i * max_stage_count + j] = classifier->haar_feature->rect[0].r.width;
            classifier_rect0_height[i * max_stage_count + j] = classifier->haar_feature->rect[0].r.height;
            classifier_rect0_weight[i * max_stage_count + j] = classifier->haar_feature->rect[0].weight;

            classifier_rect1_x[i * max_stage_count + j] = classifier->haar_feature->rect[1].r.x;
            classifier_rect1_y[i * max_stage_count + j] = classifier->haar_feature->rect[1].r.y;
            classifier_rect1_width[i * max_stage_count + j] = classifier->haar_feature->rect[1].r.width;
            classifier_rect1_height[i * max_stage_count + j] = classifier->haar_feature->rect[1].r.height;
            classifier_rect1_weight[i * max_stage_count + j] = classifier->haar_feature->rect[1].weight;

            if(!cascade->hid_cascade->stage_classifier[i].two_rects) {
                classifier_rect2_x[i * max_stage_count + j] = classifier->haar_feature->rect[2].r.x;
                classifier_rect2_y[i * max_stage_count + j] = classifier->haar_feature->rect[2].r.y;
                classifier_rect2_width[i * max_stage_count + j] = classifier->haar_feature->rect[2].r.width;
                classifier_rect2_height[i * max_stage_count + j] = classifier->haar_feature->rect[2].r.height;
                classifier_rect2_weight[i * max_stage_count + j] = classifier->haar_feature->rect[2].weight;
            }
            else {
                classifier_rect2_x[i * max_stage_count + j] = 0;
                classifier_rect2_y[i * max_stage_count + j] = 0;
                classifier_rect2_width[i * max_stage_count + j] = 0;
                classifier_rect2_height[i * max_stage_count + j] = 0;
                classifier_rect2_weight[i * max_stage_count + j] = 0;
            }

            classifier_threshold[i * max_stage_count + j] = classifier->threshold[0];
            classifier_alpha0[i * max_stage_count + j] = classifier->alpha[0];
            classifier_alpha1[i * max_stage_count + j] = classifier->alpha[1];
        }
    }

    FaceDetectionGPUCore(img_cols, img_rows, img_width_step, sum_width, sum_height, sum_width_step,
                         cascade_origin_window_size_width, cascade_origin_window_size_height,
                         scale_factor, cascade_count, max_stage_count,
                         image_data, sum_data, sqsum_data, stage_count, stage_threshold,
                         classifier_rect0_x, classifier_rect0_y, classifier_rect0_width, classifier_rect0_height, classifier_rect0_weight,
                         classifier_rect1_x, classifier_rect1_y, classifier_rect1_width, classifier_rect1_height, classifier_rect1_weight,
                         classifier_rect2_x, classifier_rect2_y, classifier_rect2_width, classifier_rect2_height, classifier_rect2_weight,
                         classifier_alpha0, classifier_alpha1, classifier_threshold);

    return NULL;
}
